# ComplexTypeBinder.java

    package org.codefilarete.stalactite.sql.statement.binder;

    import java.sql.PreparedStatement;
    import java.sql.ResultSet;
    import java.sql.SQLException;

    import org.codefilarete.stalactite.sql.ddl.SqlTypeRegistry;
    import org.codefilarete.tool.function.ThrowingConverter;

    /**
     * Frame for handling unexpected type (from the library) that needs to be persisted into a single column.
     * Acts more as a how-to or proof-of-concept than a really value-added class because there's quite no magic here : it only composes a binder and 2
     * converters for reading and writing to a column. Moreover it can be done directly by creating your own {@link ParameterBinder} and implementing
     * its methods.
     *
     * @param <C> the mapped type
     * @author Guillaume Mary
     * @see SqlTypeRegistry
     * @see ColumnBinderRegistry
     */
    public class ComplexTypeBinder<C> implements ParameterBinder<C> {

    	private final NullAwareParameterBinder<C> convertingBinder;

    	/**
    	 * Creates a binder for persisting C objects (handling eventually null values).
    	 * Instance should be registered into a {@link SqlTypeRegistry} and {@link
    	 * ColumnBinderRegistry}
    	 *
    	 * @param lowerBinder the binder that interacts with {@link PreparedStatement} and {@link ResultSet}
    	 * @param toObjectConverter a converter applied on read value given by lower binder when reading from a {@link ResultSet}
    	 * @param toDatabaseConverter a converter applied on input value, then result is passed to lower binder when writing to {@link PreparedStatement}
    	 * @param <P> the intermediary type
    	 */
    	public <P> ComplexTypeBinder(ParameterBinder<P> lowerBinder,
    								 ThrowingConverter<P, C, RuntimeException> toObjectConverter,
    								 ThrowingConverter<C, P, RuntimeException> toDatabaseConverter) {
    		convertingBinder = new NullAwareParameterBinder<>(new LambdaParameterBinder<>(
    				// we simply need some conversion after reading and before writing
    				lowerBinder.thenApply(toObjectConverter::convert), lowerBinder.preApply(toDatabaseConverter::convert)
    		));
    	}

    	@Override
    	public void set(PreparedStatement preparedStatement, int valueIndex, C value) throws SQLException {
    		convertingBinder.set(preparedStatement, valueIndex, value);
    	}

    	@Override
    	public Class<C> getType() {
    		return convertingBinder.getType();
    	}

    	@Override
    	public C doGet(ResultSet resultSet, String columnName) {
    		return convertingBinder.get(resultSet, columnName);
    	}
    }